{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "5a1368f0",
   "metadata": {},
   "source": [
    "# 作业5：RNN 生成模型"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "997082ed",
   "metadata": {},
   "source": [
    "以 `data/names.txt` 中的英文名作为训练集，利用 RNN 或 LSTM 等方法对字母序列数据进行建模，然后使用拟合的模型随机生成20个名字。本次作业为开放式，不指定各类超参数（如网络结构、学习率、迭代次数等），但需提供必要的输出和诊断结果支持你的选择（如模型是否收敛、效果评价等）。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "85b11e45",
   "metadata": {},
   "source": [
    "提示：可以参照 `lec12-rnn-generation.zip` 中的代码，但注意英文名不需要像中文那样构建字典，因为可以直接使用26个字母作为字典。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "f0747bcc",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "5000 5% 2.0595\n",
      "10000 10% 1.6436\n",
      "15000 15% 2.2185\n",
      "20000 20% 1.6577\n",
      "25000 25% 1.5852\n",
      "30000 30% 2.5128\n",
      "35000 35% 1.7446\n",
      "40000 40% 2.1354\n",
      "45000 45% 1.9849\n",
      "50000 50% 2.2516\n",
      "55000 55% 1.2622\n",
      "60000 60% 2.0111\n",
      "65000 65% 2.5587\n",
      "70000 70% 1.3974\n",
      "75000 75% 1.6821\n",
      "80000 80% 1.4658\n",
      "85000 85% 2.6155\n",
      "90000 90% 1.3561\n",
      "95000 95% 1.6146\n",
      "100000 100% 2.5544\n"
     ]
    },
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "donestonnerr\n",
      "uleynerronto\n",
      "tonelandonte\n",
      "jondersooden\n",
      "rowelleysond\n",
      "veringersher\n",
      "corelleytond\n",
      "xondonsonner\n",
      "veringersher\n",
      "charrowerthe\n",
      "marrinstorne\n",
      "ingersonderr\n",
      "warelleysend\n",
      "jernonsonder\n",
      "ramckerryent\n",
      "veringershen\n",
      "sondeardyers\n",
      "yarrowertond\n",
      "burneylendon\n",
      "xondeanderto\n"
     ]
    }
   ],
   "source": [
    "import time\n",
    "import numpy as np\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import itertools\n",
    "import collections\n",
    "import matplotlib.pyplot as plt\n",
    "import string\n",
    "import random\n",
    "\n",
    "# 读取数据\n",
    "with open(\"data/names.txt\", \"r\") as file:\n",
    "    names = [line.strip().lower() for line in file.readlines() if line.strip().isalpha()]\n",
    "\n",
    "# 计算字符频率\n",
    "chars = [list(name) for name in names]\n",
    "chars_flatten = list(itertools.chain(*chars))\n",
    "freq = collections.Counter(chars_flatten)\n",
    "\n",
    "# 准备数据\n",
    "charset = sorted(set(chars_flatten))  # 字符集\n",
    "charset_size = len(charset) + 1  # 字符集大小，加上结束符\n",
    "\n",
    "# 字符到索引的映射\n",
    "def char2index(char):\n",
    "    return charset.index(char)\n",
    "\n",
    "# 字符到张量的映射（One-hot编码）\n",
    "def char2tensor(char):\n",
    "    tensor = torch.zeros(1, charset_size)\n",
    "    tensor[0, char2index(char)] = 1\n",
    "    return tensor\n",
    "\n",
    "# 名字到张量的映射\n",
    "def name2tensor(name):\n",
    "    tensor = torch.zeros(len(name), 1, charset_size)\n",
    "    for i, char in enumerate(name):\n",
    "        tensor[i][0][char2index(char)] = 1\n",
    "    tensor = torch.cat((tensor, torch.zeros(1, 1, charset_size)), 0)  # 添加结束符\n",
    "    return tensor\n",
    "\n",
    "# RNN模型\n",
    "class RNN(nn.Module):\n",
    "    def __init__(self, input_size, hidden_size, output_size):\n",
    "        super(RNN, self).__init__()\n",
    "        self.hidden_size = hidden_size\n",
    "        self.i2h = nn.Linear(input_size + hidden_size, hidden_size)\n",
    "        self.i2o = nn.Linear(input_size + hidden_size, output_size)\n",
    "        self.o2o = nn.Linear(hidden_size + output_size, output_size)\n",
    "        self.dropout = nn.Dropout(0.1)\n",
    "        self.softmax = nn.LogSoftmax(dim=1)\n",
    "\n",
    "    def forward(self, input, hidden):\n",
    "        combined = torch.cat((input, hidden), 1)\n",
    "        hidden = self.i2h(combined)\n",
    "        output = self.i2o(combined)\n",
    "        output_combined = torch.cat((hidden, output), 1)\n",
    "        output = self.o2o(output_combined)\n",
    "        output = self.dropout(output)\n",
    "        output = self.softmax(output)\n",
    "        return output, hidden\n",
    "\n",
    "    def init_hidden(self):  # 修改方法名称\n",
    "        return torch.zeros(1, self.hidden_size)\n",
    "\n",
    "\n",
    "# 增加隐藏层的大小\n",
    "n_hidden = 256  # 增加隐藏层大小\n",
    "\n",
    "rnn = RNN(charset_size, n_hidden, charset_size)\n",
    "\n",
    "learning_rate = 0.01  # 调整学习率\n",
    "optimizer = torch.optim.Adam(rnn.parameters(), lr=learning_rate)  # 使用Adam优化器\n",
    "\n",
    "n_iters = 200000  # 增加迭代次数\n",
    "print_every = 10000\n",
    "plot_every = 1000\n",
    "all_losses = []\n",
    "current_loss = 0\n",
    "\n",
    "\n",
    "# 训练函数\n",
    "def train(rnn, input_line_tensor, target_line_tensor, criterion, optimizer):\n",
    "    hidden = rnn.init_hidden()\n",
    "\n",
    "    rnn.zero_grad()\n",
    "\n",
    "    loss = 0\n",
    "    for i in range(input_line_tensor.size(0)):\n",
    "        output, hidden = rnn(input_line_tensor[i], hidden)\n",
    "        if i < target_line_tensor.size(0):\n",
    "            l = criterion(output, target_line_tensor[i].unsqueeze(0))\n",
    "            loss += l\n",
    "\n",
    "    loss.backward()\n",
    "    optimizer.step()\n",
    "\n",
    "    return output, loss.item() / input_line_tensor.size(0)\n",
    "\n",
    "# 生成名称的函数\n",
    "def generate(rnn, max_length=11):\n",
    "    with torch.no_grad():\n",
    "        start_letter = random.choice(string.ascii_lowercase)\n",
    "        input = char2tensor(start_letter).view(1, -1)  # 调整为 [1 x charset_size]\n",
    "        hidden = rnn.init_hidden()\n",
    "\n",
    "        output_name = start_letter\n",
    "\n",
    "        for i in range(max_length):\n",
    "            # 确保 input 的形状为 [1 x charset_size]\n",
    "            input_correct_shape = input.view(1, charset_size)\n",
    "            output, hidden = rnn(input_correct_shape, hidden)\n",
    "            topv, topi = output.topk(1)\n",
    "            topi = topi[0][0]\n",
    "            if topi == charset_size - 1:\n",
    "                break\n",
    "            else:\n",
    "                letter = charset[topi]\n",
    "                output_name += letter\n",
    "                input = char2tensor(letter).view(1, -1)  # 更新 input 张量\n",
    "\n",
    "        return output_name\n",
    "\n",
    "# 定义和训练模型\n",
    "n_hidden = 128\n",
    "rnn = RNN(charset_size, n_hidden, charset_size)\n",
    "criterion = nn.NLLLoss()\n",
    "learning_rate = 0.005  # 调整学习率\n",
    "optimizer = torch.optim.SGD(rnn.parameters(), lr=learning_rate)\n",
    "\n",
    "n_iters = 100000\n",
    "print_every = 5000\n",
    "plot_every = 1000\n",
    "all_losses = []\n",
    "current_loss = 0\n",
    "\n",
    "# 训练过程\n",
    "for iter in range(1, n_iters + 1):\n",
    "    name = random.choice(names)\n",
    "    if len(name) > 1:  # 确保名字至少有两个字符\n",
    "        input_line_tensor = name2tensor(name[:-1])  # 使用除最后一个字符之外的所有字符\n",
    "        target_line_tensor = torch.LongTensor([char2index(c) for c in name[1:]] + [charset_size - 1])  # 从第二个字符开始到结束符\n",
    "\n",
    "        output, loss = train(rnn, input_line_tensor, target_line_tensor, criterion, optimizer)\n",
    "        current_loss += loss\n",
    "\n",
    "\n",
    "    # 打印进度\n",
    "    if iter % print_every == 0:\n",
    "        print('%d %d%% %.4f' % (iter, iter / n_iters * 100, loss))\n",
    "\n",
    "    # 记录损失\n",
    "    if iter % plot_every == 0:\n",
    "        all_losses.append(current_loss / plot_every)\n",
    "        current_loss = 0\n",
    "\n",
    "# 显示损失曲线\n",
    "plt.figure()\n",
    "plt.plot(all_losses)\n",
    "plt.show()\n",
    "\n",
    "# 生成一些名字\n",
    "for _ in range(20):\n",
    "    print(generate(rnn))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4a5c5591",
   "metadata": {},
   "source": [
    "模型收敛情况：\n",
    "\n",
    "损失值随训练次数增加而整体下降，表明模型正逐渐适应训练数据，这是模型收敛的一个重要迹象。 尽管损失值在整个训练过程中有波动，但这种波动在深度学习模型训练中是常见的，并不一定意味着模型未收敛。相反，这可能表明模型在不断探索并适应数据的复杂性。\n",
    "\n",
    "效果评价：\n",
    "\n",
    "生成的名字具有一定的多样性和创造性，这说明模型已经学会了从字符级别构建和生成新的名字。 模型能够生成具有实际意义的名字，尽管它们可能与常规名字有所不同。这表明模型在学习字符序列和名称结构方面取得了显著进展。 综合考虑损失值下降和生成的名字，可以认为模型已经在一定程度上学习到了名字的基本结构和变化规律。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cc83af96",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
